import { Dep } from './Dep.js'

// 第二步：实现观察者 -> 遍历把对象的所有键设置为响应式，实现 observe 方法，把方法封装成 Observer 类
class Observer {
  constructor(value) {
    this.value = value

    // 这里不观察数组，只观察对象
    if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
      this.walk(value)
    }
  }

  // 遍历对象把对象所有属性转换成响应式(getter/setter)
  walk(obj) {
    Object.keys(obj).forEach(key => this.convert(key, obj[key]))
  }

  // 执行具体的转换方法，单个属性转换
  convert(key, val) {
    defineReactive(this.value, key, val)
  }
}

// 第一步：封装 Object.defineProperty 得到一个把对象的某个属性转换成响应式的对象
function defineReactive(data, key, val) {
  // 新增 此处 val 如果是对象，需要递归这个子属性
  if (typeof val === 'object' && val !== null) {
    new Observer(val)
  }
  
  // 新增第三步：修改 defineReactive() 方法，实例化一个订阅管理者
  // 在 getter 中调用 dep.depend() 收集依赖，在 setter 中调用 dep.notify() 通知所有依赖去执行变更
  let dep = new Dep()
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      // 新增
      dep.depend()
      return val
    },
    set: function(newVal) {
      if (val === newVal) return
      val = newVal
      // 新增
      dep.notify()
    }
  })
}

// 对外暴露一个把一个对象转换成响应式的接口
export function observe(data) {
  if (!data || typeof data !== 'object') return
  return new Observer(data)
}
